package logic;

import java.util.ArrayList;
import java.util.Hashtable;

import javax.swing.JLabel;


/* THIS CLASS ONLY CHECKS TOP BOTTOM RIGHT AND LEFT POSITIONS IN A BLOCK
 * EXAMPLE
 * 			|	  |TOP|	   |
 * 			|RIGHT|XXX|LEFT|
 * 			|	  |BOT|    |
 * Esta clase comprueba las adyacencias de cada uno de los tokens
 */


public class SintaxAnalizer{

	//constante usada para indicar que un token no tiene entradas o tiene mas de una
	public static final int _DOUBLE_NO_NULL = -1;
	//variable para pedir casos estrictos como un var despues de un if
	private boolean restricted = false;
	// para guardar las coordenadas esperadas para los tokens restringidos
	private Integer	_RestrictedX = 0;
	private Integer	_RestrictedY = 0;
	/** 
	 * Verifica que las adyacencias sean correctas
	 */
	
	// es la primera funcion divide las comprobaciones de acuerdo con el type
	public boolean addBlock( int rRow , int rCol , Block pBloque, Pestanna pPestanna ){
		//Si la ubicacion se encuentra disponible
		if ( pPestanna.getCuadricula()[rRow][rCol] == null ){
			// si no se esta esperando un token restringido
			if (restricted){
				if (pBloque.getType() == Constantes.TYPE_VAR && rRow == _RestrictedX && rCol == _RestrictedY){
					restricted = false;
					pPestanna.getCuadricula()[rRow][rCol] = pBloque;
					return true;
				}
				return false;
			//si es una pipe compruebe segun pipes
			}else if ( pBloque.getType() == Constantes.TYPE_PIPE 
					&& checkPipe(rRow , rCol , (Pipe)pBloque, pPestanna ) ){
				pPestanna.getCuadricula()[rRow][rCol] = pBloque;
			//segun commandos 
			}else if ( pBloque.getType() == Constantes.TYPE_COMMAND 
					&& checkCommand(rRow , rCol , (Command)pBloque, pPestanna ) ){
				pPestanna.getCuadricula()[rRow][rCol] = pBloque;
				//si es uno de los siguientes comandos el proximo token necesariamente debe ser un var en bot
				if ( ((Command)pBloque).get_Id() == Constantes.ID_LED
					|| ((Command)pBloque).get_Id() == Constantes.ID_IF
					|| ((Command)pBloque).get_Id() == Constantes.ID_FOTOREST
					|| ((Command)pBloque).get_Id() == Constantes.ID_POTENC
					|| ((Command)pBloque).get_Id() == Constantes.ID_BOTON){
					restricted = true;
					_RestrictedX = rRow;
					_RestrictedY = rCol+1;
					//si es uno de los siguientes comandos el proximo token necesariamente debe ser un var en top
				}else if ( ((Command)pBloque).get_Id() == Constantes.ID_DELAY ){
					restricted = true;
					_RestrictedX = rRow;
					_RestrictedY = rCol-1;
				}
				// si es un var compruebe segun vars
			}else if ( pBloque.getType() == Constantes.TYPE_VAR
					&& checkVar(rRow , rCol , (Var)pBloque, pPestanna ) ){
				pPestanna.getCuadricula()[rRow][rCol] = pBloque;
			}else{
				return false;
			}
			return true;
		};
		return false;
	}
	
	//comprueba que haya una y solo una entrada de flujo en caso verdadero arroja la posicion en caso falso la constante doubl no null
	private int checkNullity( int rRow , int rCol , Block pBloque, Pestanna pPestanna ){
		int _pos = _DOUBLE_NO_NULL;
		//check nullity
		for ( int pos = 0 ;  pos < Constantes.CNT_POS ; pos++ ){
			//basicamente que tengan extremos en comun ej: NW-WE
			if ( pPestanna.exist( rRow + pos%3 - 1 , rCol + pos/3 - 1 ) && pos%2 == 1
					&& !( pPestanna.getAdyacente(rRow, rCol, pos).getType() == Constantes.TYPE_VAR
							&& ( pos == Constantes.POS_E || pos == Constantes.POS_W ) 
							&& (pBloque.getType() == Constantes.TYPE_VAR || 
								(pBloque.getType() == Constantes.TYPE_PIPE && ((Pipe)pBloque).get_Id() == Constantes.ID_NS ) ) )
					&& !( pPestanna.getAdyacente(rRow, rCol, pos).getType() ==  Constantes.TYPE_PIPE
							&& ((Pipe)pPestanna.getAdyacente(rRow, rCol, pos)).get_Id() == Constantes.ID_NS
							&& ( pos == Constantes.POS_E || pos == Constantes.POS_W )) 
					&& !( pPestanna.getAdyacente(rRow, rCol, pos).getType() ==  Constantes.TYPE_PIPE
						&& ((Pipe)pPestanna.getAdyacente(rRow, rCol, pos)).get_Id() == Constantes.ID_WE
						&& pBloque.getType() == Constantes.TYPE_PIPE
						&& ( ((Pipe)pBloque).get_Id() == Constantes.ID_WE || ((Pipe)pBloque).get_Id() == Constantes.ID_SW 
								|| ((Pipe)pBloque).get_Id() == Constantes.ID_SE)
						&& ( pos == Constantes.POS_N || pos == Constantes.POS_S ))
					&& !( pPestanna.getAdyacente(rRow, rCol, pos).getType() ==  Constantes.TYPE_PIPE
						&& ( ((Pipe)pPestanna.getAdyacente(rRow, rCol, pos)).get_Id() == Constantes.ID_NW
							|| ((Pipe)pPestanna.getAdyacente(rRow, rCol, pos)).get_Id() == Constantes.ID_SW )
						&& ( pos == Constantes.POS_W ))
					&& !( pPestanna.getAdyacente(rRow, rCol, pos).getType() ==  Constantes.TYPE_PIPE
						&& ( ((Pipe)pPestanna.getAdyacente(rRow, rCol, pos)).get_Id() == Constantes.ID_NE
							|| ((Pipe)pPestanna.getAdyacente(rRow, rCol, pos)).get_Id() == Constantes.ID_SE )
						&& ( pos == Constantes.POS_E )) ){
				
				// guarda la entrada
				if ( _pos == _DOUBLE_NO_NULL ){
					_pos = pos;
				// si antes habia una entrada entonces lance la constante de error
				}else{
					return _DOUBLE_NO_NULL;
				}
			}
		}
		return _pos;		
	}
	
	//check pipe basicamente que tengan extremos en comun
	private boolean checkPipe( int rRow , int rCol , Pipe pBloque, Pestanna pPestanna ){
		int posNoNull = checkNullity(rRow, rCol, pBloque, pPestanna);
		if ( posNoNull != _DOUBLE_NO_NULL ){
			Block adyacente = pPestanna.getAdyacente(rRow, rCol, posNoNull);
			if (adyacente.getType() == Constantes.TYPE_COMMAND && ((Command)adyacente).get_Id() == Constantes.ID_END ){
				return false;
			}
			if ( posNoNull == Constantes.POS_W){
				if( adyacente.getType() == Constantes.TYPE_PIPE){
					return		(	((Pipe)adyacente).get_Id() == Constantes.ID_WE
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_NE
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_SE)
							&&	(pBloque.get_Id() == Constantes.ID_WE
									|| pBloque.get_Id() == Constantes.ID_SW 
									|| pBloque.get_Id() == Constantes.ID_NW);
				}else if ( pPestanna.getAdyacente(rRow, rCol, posNoNull).getType() == Constantes.TYPE_COMMAND ){
					return true;
				}else{
					return ( pPestanna.exist(rRow-1, rCol-1) 
						&& ( pPestanna.get_Cuadricula()[rRow-1][rCol-1].getType() == Constantes.TYPE_COMMAND
							&& ((Command)pPestanna.get_Cuadricula()[rRow-1][rCol-1]).get_Id() == Constantes.ID_IF
						|| pPestanna.get_Cuadricula()[rRow-1][rCol-1].getType() == Constantes.TYPE_VAR ));
				}
			}else if ( posNoNull == Constantes.POS_N){
				if( adyacente.getType() == Constantes.TYPE_PIPE){
					return		(	((Pipe)adyacente).get_Id() == Constantes.ID_NS
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_SE
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_SW)
							&&	(	pBloque.get_Id() == Constantes.ID_NE
									|| pBloque.get_Id() == Constantes.ID_NS
									|| pBloque.get_Id() == Constantes.ID_NW);
				}else if ( pPestanna.getAdyacente(rRow, rCol, posNoNull).getType() == Constantes.TYPE_COMMAND ){
					return 	((Command)pPestanna.getAdyacente(rRow, rCol, posNoNull)).get_Id() == Constantes.ID_INICIO;
				}else{
					return false;
				}
			}else if ( posNoNull == Constantes.POS_E ){
				if( adyacente.getType() == Constantes.TYPE_PIPE){
					return		(	((Pipe)adyacente).get_Id() == Constantes.ID_NW
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_SW
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_WE)
							&&	(pBloque.get_Id() == Constantes.ID_NE
									|| pBloque.get_Id() == Constantes.ID_SE
									|| pBloque.get_Id() == Constantes.ID_WE);
				}else if ( pPestanna.getAdyacente(rRow, rCol, posNoNull).getType() == Constantes.TYPE_COMMAND ){
					return 	false;
				}else{
					return false;
				}
			}else if ( posNoNull == Constantes.POS_S ){
				if( adyacente.getType() == Constantes.TYPE_PIPE){
					return		(	((Pipe)adyacente).get_Id() == Constantes.ID_NW
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_NS
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_NE)
							&&	(pBloque.get_Id() == Constantes.ID_SW
									|| pBloque.get_Id() == Constantes.ID_SE
									|| pBloque.get_Id() == Constantes.ID_NS);
				}else if ( pPestanna.getAdyacente(rRow, rCol, posNoNull).getType() == Constantes.TYPE_COMMAND ){
					return 	false;
				}else{
					return false;
				}
			}
		}
		return false;		
	}
	
	
	//chech var comprueba todos los casos que puede ocupar una variable
	private boolean checkVar( int rRow , int rCol , Var pBloque, Pestanna pPestanna ){
		int posNoNull = checkNullity(rRow, rCol, pBloque, pPestanna);
		if ( posNoNull != _DOUBLE_NO_NULL ){
			Block adyacente = pPestanna.getAdyacente(rRow, rCol, posNoNull);
			if ( posNoNull == Constantes.POS_N){
				System.out.println(adyacente.getType());
				if( adyacente.getType() == Constantes.TYPE_PIPE){
					return	((Pipe)adyacente).get_Id() == Constantes.ID_WE;
				}else if ( pPestanna.getAdyacente(rRow, rCol, posNoNull).getType() == Constantes.TYPE_COMMAND ){
					return 		((Command)adyacente).get_Id() == Constantes.ID_IF
							||	((Command)adyacente).get_Id() == Constantes.ID_LED
							|| ((Command)adyacente).get_Id() == Constantes.ID_FOTOREST
							|| ((Command)adyacente).get_Id() == Constantes.ID_BOTON
							|| ((Command)adyacente).get_Id() == Constantes.ID_POTENC
							|| ((Command)adyacente).get_Id() == Constantes.ID_7SEG;
				}else if ( pPestanna.getAdyacente(rRow, rCol, posNoNull).getType() == Constantes.TYPE_VAR){
					Block adyAdy = pPestanna.get_Cuadricula()[rRow][rCol-2];
					if ( adyAdy.getType() == Constantes.TYPE_COMMAND ){
						return ((Command)adyAdy).get_Id() == Constantes.ID_IF;
					}else{
						return adyAdy.getType() == Constantes.TYPE_VAR;
					}
				}
			}else if ( posNoNull == Constantes.POS_S ){
				if( adyacente.getType() == Constantes.TYPE_PIPE){
					return	((Pipe)adyacente).get_Id() == Constantes.ID_WE;
				}else if ( pPestanna.getAdyacente(rRow, rCol, posNoNull).getType() == Constantes.TYPE_COMMAND ){
					return 	((Command)adyacente).get_Id() == Constantes.ID_DELAY
							|| ((Command)adyacente).get_Id() == Constantes.ID_FOTOREST
							|| ((Command)adyacente).get_Id() == Constantes.ID_BOTON
							|| ((Command)adyacente).get_Id() == Constantes.ID_POTENC
							|| ((Command)adyacente).get_Id() == Constantes.ID_7SEG;
				}
			}
		}
		return false;
	}
	
	
	//check commands igual que los pipes solo posicion
	private boolean checkCommand( int rRow , int rCol , Command pBloque, Pestanna pPestanna ){
		int posNoNull = checkNullity(rRow, rCol, pBloque, pPestanna);
		if ( posNoNull != _DOUBLE_NO_NULL ){
			Block adyacente = pPestanna.getAdyacente(rRow, rCol, posNoNull);
			if (adyacente.getType() == Constantes.TYPE_COMMAND && ((Command)adyacente).get_Id() == Constantes.ID_END ){
				return false;
			}
			if ( posNoNull == Constantes.POS_W){
				if( adyacente.getType() == Constantes.TYPE_PIPE ){
					return			((Pipe)adyacente).get_Id() == Constantes.ID_WE
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_NE
									|| ((Pipe)adyacente).get_Id() == Constantes.ID_SE;
				}else{
					return pPestanna.getAdyacente(rRow, rCol, posNoNull).getType() == Constantes.TYPE_COMMAND;
				}
			}else if ( posNoNull == Constantes.POS_S && pBloque.get_Id() == Constantes.ID_MODIF){
				return pPestanna.getAdyacente(rRow, rCol, posNoNull).getType() == Constantes.TYPE_COMMAND
						&& ((Command)pPestanna.getAdyacente(rRow, rCol, posNoNull)).get_Id() == Constantes.ID_LED;
			}
		}
		return false;
	}

	
	//utilizados por los deshacer y rehacer
	public void setRestricted(boolean restricted) {
		this.restricted = restricted;
	}

	public boolean isRestricted() {
		return restricted;
	}
	


}
